Menu Search
Jump to the content X X
SmashingConf London Avatar

We use ad-blockers as well, you know. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. our upcoming SmashingConf London, dedicated to all things web performance.

CSS-Driven Internationalization In JavaScript

Writing front-end code often requires developers to address the problem of internationalization at some level. Despite the current standard1, which introduces a bunch of new tags, simply adding support for different languages in a JavaScript plugin is still complicated. As a result, for any new project, you have to build a solution from scratch or adapt various existing APIs from other plugins that you use.

In this article, I’ll describe my approach with better-dom2 to solve the internationalization problem. Since the last article about this, “Writing a Better JavaScript Library for the DOM3,” I’ve revisited the concept to solve the issues raised through feedback.

Further Reading on SmashingMag: Link4

The solution was originally intended to be a set of internationalization APIs for plugins, extensions, etc. It doesn’t rely heavily on the better-dom library, so it could be adapted to any existing JavaScript library.

A Typical Solution To The Problem Link

Different APIs exist for changing languages in JavaScript. Most of them contain three main functions:

  1. The first function registers a localized string by key and language.
  2. The second sets a localized string for a particular element.
  3. The third is used to change the current language.

Let’s look at an example based on the Validator plugin from the jQuery Tools library. The plugin supports the localization of validation errors via JavaScript. Default error messages are stored in the $.tools.validator.messages object.


Default error messages are stored in the $.tools.validator.messages object. (View large version9)

For keys, the plugin uses CSS selectors (to simplify the code). If you want to provide error messages in other languages, you would use the $.tools.validator.localize method, as follows:

$.tools.validator.localize("fi", {
	":email" : "Virheellinen sähköpostiosoite",
	":number" : "Arvon on oltava numeerinen",
	"[max]" : "Arvon on oltava pienempi, kuin $1",
	"[min]" : "Arvon on oltava suurempi, kuin $1",
	"[required]" : "Kentän arvo on annettava"

This method populates for Finnish localization. The $.tools.validator.messages object would look like this:


The $.tools.validator.messages object populated for Finnish localization (View large version11)

Now, if you want to use the Finnish localization in your form, then you need to change the default language (English) via the lang configuration option:

$("#myForm").validator({lang: "fi"});

The plugin implements the typical solution that we have at present. Having looked at approaches similar to this one, I found several common shortcomings:

  1. Obtrusive You have to add a JavaScript function call if the current page’s language is different from the default (usually English) used in a plugin.
  2. Ineffective To change a language dynamically, you have to call a particular function and then touch the DOM of every related element to update the innerHTML, depending on the new language.
  3. Hard to maintain Every plugin has its own set of APIs.

The first shortcoming is the most critical. If your project has a lot of components, switching to the non-default language on initial page load for every plugin will be painful. If the project fetches data using AJAX calls, then the same steps would have to be done for future content as well. Let’s try to solve all of these shortcomings. First, we need to go through the technical stuff.

The :lang Pseudo-Class Link

Remember the :lang pseudo-class from CSS2? It’s rarely used, but when I first read about it in the specification12, I was curious what the standard’s authors intended to solve with it:

If the document language specifies how the human language of an element is determined, it is possible to write selectors in CSS that match an element based on its language.

A typical example cited in the specification is the symbol for quotations. The symbol varies between languages. To address this for the <q> element13 (which marks up a short quotation, usually wrapped in quotation marks), we can use the :lang pseudo-class:

:lang(fr) > q { quotes: '« ' ' »' }
:lang(de) > q { quotes: '»' '«' '\2039' '\203A' }

An important difference between the :lang pseudo-class and a simple attribute selector like [lang=fr] is that the latter matches only elements that have the lang attribute. Therefore, the :lang pseudo-class is always safer than the attribute variant because it works properly even if the :lang attribute has not been set on an element.

The example above illustrates how to change the representation of content according to the current language using CSS. This is important because it enables us to put the logic related to changing the language into CSS.

The example of the symbol for quotation marks is nice, but it addresses a small number of cases and so can’t be used in typical scenarios — ordinary strings are usually very different in different languages. We need a trick that allows us to change an element’s content completely.

Changing an Element’s Content With CSS Link

Browsers that support the CSS2 specification introduced pseudo-elements, which, rather than describing a particular state like pseudo-classes, enable us to style certain parts of a document.

Note that Internet Explorer 8 has a known issue in its implementation in that it does not support the double-colon syntax for defining pseudo-elements. The problem was fixed in Internet Explorer 9, so if you need to support version 8, make sure to use the single-colon syntax for any pseudo-element.

The real gems are ::before and ::after, which enable you to add extra content before or after an element’s innerHTML. They might look simple, but they have a ton of use cases that solve problems in a very clean way.

Let’s start with the basics. Both ::before and ::after introduce a CSS property, content. This new property defines what content to prepend or append to an element’s innerHTML. The value of the content attribute may be any of the following:

  • text string (but not an HTML string),
  • image,
  • counter,
  • attribute value(s).

Our main interest is adding a text string. Imagine we have CSS like this:

#hello::before {
   content: "Hello ";

If the element with the ID of hello contains the string world, then the browser would display Hello world.

<p id="hello">world</p>

We could rewrite our CSS using the attr function:

#hello::before {
   content: attr(id) " ";

Then, the element would display hello world in lowercase, because the id attribute has a lowercased string value.

Now, imagine that the hello element didn’t have any inner content. We could change its representation completely using CSS. This becomes handy when we use the trick in combination with the :lang pseudo-class:

#hello::before {
   content: "Hello";
#hello:lang(de)::before {
   content: "Hallo";
#hello:lang(ru)::before {
   content: "Привет";

Our element hello will now change according to the current web page’s language — no need to call any function to change its representation according to the current web page’s language. The localization is handled by the value of the lang attribute on the <html> element and several extra CSS rules. This is what I call CSS-driven internationalization.

CSS-Driven Internationalization: Improved! Link

Since publishing the original idea, I’ve heard several people complain that those rules could add a lot of CSS. Because my initial goal was to use it for small JavaScript plugins, I didn’t even think it could be used widely on the page. However, the philosophy of CSS is to contain the presentation logic, and I was trying to use it to store various multilingual strings, which actually belong to the web page’s content. It didn’t seem right.

After some thinking, I developed an improved version that solves this issue. Instead of putting text strings into CSS, I use the attr function to read a language-specific data-i18n-* attribute that contains a localized string. This restricts the number of CSS rules we can add: one rule per new language.

Let’s rewrite the localization of the hello element above with this improved method. This time, let’s give our web page some global CSS to support German and Russian, in addition to English:

/* English (default language)*/
[data-i18n]::before {
   content: attr(data-i18n);
/* German */
[data-i18n-de]:lang(de)::before {
   content: attr(data-i18n-de);
/* Russian */
[data-i18n-ru]:lang(ru)::before {
   content: attr(data-i18n-ru);

Note that the code above doesn’t contain any string constant: The CSS rules are generic.

Now, instead of putting localized text strings into CSS, let’s add several custom language-specific data-* attributes that contain the appropriate values. Our hello element should look like the following, which will display different content according to the current web page’s language:

<p id="hello" data-18n="Hello" data-i18n-de="Hallo" data-i18n-ru="Привет"><p>

That’s it! We’re left with minimal extra CSS, which describes only the global logic for changing an element’s representation according to the current language, and our localized strings are fully HTML.

Building A High-Level API Link

In better-dom, there are two functions to support CSS-driven internationalization: $Element.prototype.i18n and DOM.importStrings. The first function changes the localized string for a particular element. To keep it simple, I usually use the English strings as keys and default values. It makes the JavaScript more readable and easier to understand. For instance:


This sets a localized Hello string as the inner content of myelement, where myelement is an instance of the $Element class, which happens to be a wrapper type for a native DOM element in better-dom. The line above does several things behind the scenes:

  • It determines the current set of registered languages.
  • For each language, it reads a string with the key Hello in the internal storage of registered localizations, and it uses the value to set an appropriate data-i18n-* attribute for the element.
  • It cleans up the element’s innerHTML to prevent a weird result from displaying.

You can see the source code of $Element.prototype.i18n on GitHub. The goal of this i18n method is to update our custom language-specific data-* attributes. For example:

<p id="hello"><p>

After the call, this empty element would become the following, if we have registered all of the localized strings for German and Russian:

<p id="hello" data-i18n="Hello" data-i18n-de="Hallo" data-i18n-ru="Привет"><p>

Additionally, the i18n method supports an optional second argument, a key-value map of the variables:

// Use {varName} in the string template to define
// various parts of a localized string.
myelement.i18n("Hello {user}", {user: username});

// Use array and {varNumber} to define a numeric
// set of variables.
myelement.i18n("Hello {0}", [username]);

To register a localized string, use the static method DOM.importStrings to pass three arguments:

  • the target language,
  • the localized string key (usually just an English string),
  • the localized string value.

For the example above, before invoking the i18n method, we would need to make the following calls:

DOM.importStrings("de", "Hello {user}", "Hallo {user}");
DOM.importStrings("ru", "Hello {user}", "Привет {user}");

DOM.importStrings("de", "Hello {0}", "Hallo {0}");
DOM.importStrings("ru", "Hello {0}", "Привет {0}")

Behind the scenes, DOM.importStrings is going through a couple of steps. First, it checks whether the target language has been registered. If not, it adds a global CSS rule:

[data-i18n-{lang}]:lang({lang})::before {
   content: attr(data-i18n-{lang});

Then, it saves a localized string, the key-value pair, in internal storage. You can see the source code of DOM.importStrings on GitHub.

With DOM.importStrings, we can also override existing English strings. This could be useful if you need to adapt strings to your needs without changing the source code:

DOM.importStrings("en", "Hello {user}", "Hey {user}");
DOM.importStrings("en", "Hello {0}", "Hey {0}");

As you can see, these helpers free us from having to write boilerplate code and enable us to use CSS-driven internationalization on our web pages very easily.

Advantages Of CSS-Driven Internationalization Link

Let’s review the list of issues identified in the first part of the article.

Is It Unobtrusive? Link

With the original solution, we said you had to add a JavaScript function call if the current page’s language was different from the default (usually English) used in the plugin. A big advantage of CSS-driven internationalization is that it uses the :lang pseudo-class to switch to the target language. This means that having an appropriate value of the lang attribute on the <html> element is enough to choose the localized string that you need.

Therefore, with CSS-driven internationalization, you do not need to make any calls on page load, even if the web page’s language is different from the default language. So, it’s unobtrusive.

Is It Effective? Link

To change a language dynamically, you had to call a particular function and then touch the DOM of every related element to update the innerHTML, depending on the new language. Now, the representation of an element is handled by the ::before pseudo-element. To switch to another language dynamically at a global level, just change the lang attribute of the <html> element (using native APIs, for example). Or, to localize the language change, just change the lang attribute of a particular subtree.

Also, you don’t need to update the innerHTML of all related elements in order to change the current language dynamically. This is handled by CSS. So, our code is now more effective.

Is It Easy to Maintain? Link

Every plugin originally had to have its own set of APIs. A robust solution for internationalization should be a part of every serious library that touches the DOM. CSS-driven internationalization has been a part of my better-dom project since the beginning because I had to address this problem. I used it in better-form-validation14 to customize the form-validation tooltips. Later, I used it in better-dateinput-polyfill15 and better-prettydate16. Having APIs for internationalization built into the core library reduces my boilerplate code and makes it more consistent, stabler and — you guessed it — easier to maintain.

Limitations Of CSS-Driven Internationalization Link

What about cons of CSS-driven internationalization?

JavaScript Link

First, the solution depends on JavaScript. Putting localized strings into data-* attributes on static web pages isn’t a good idea because the markup would look weird, semantically speaking. Therefore, I’d recommend using a set of JavaScript APIs (like what’s described above) to make the concept workable.

Make sure to use it in parts of your pages that are not critical to SEO, because search engine crawlers will find the resulting markup difficult to index correctly. Remember that this solution was originally developed as a set of localization APIs for JavaScript plugins and extensions.

Pseudo-Elements Link

Some limitations also come from using the ::before and ::after pseudo-elements:

  1. The content property does not work on empty elements or on certain form elements, including <input> and <select>.
  2. The content property cannot display HTML tags.
  3. There is no way to localize the values of an HTML element’s attributes (such as placeholder and title).

As for the first limitation, the lack of support for empty elements is not a big problem. The elements do not contain any content, so there is nothing to localize. But the problem became real when I was working with the <input> element in better-dateinput-polyfill. To solve this, I hid the original element and added an extra <span> element as a wrapper that contained the localized string I needed to display. Not very elegant, but it worked.

The second and third limitations are easier to avoid for now. I have some ideas on how to solve them, but I don’t have use cases for them. An elegant solution is welcome, of course.

Solving Accessibility Problems Link

Update (24.06.2014): Several people have noted in the comments section below that using pseudo-elements to display localized strings has important accessibility problems. The first problem is that a content, generated via ::before and ::after is not selectable via mouse. The second is that such content is completely missed by screen readers. Therefore, I’ve improved the concept to address these issues and invite you to check out the demo17. It is not part of better-dom APIs yet, but it will be added in the nearest version.

The main difference is that instead of the pseudo-elements, the content is displayed inside of language-specific <span> elements. It is not possible to have several <span>s to be displayed at the same time because <span>s for non-current language are hidden via the display:none rule. Screen readers skip such hidden elements which is what we need exactly.

Using inner <span>s instead of pseudo-elements also fixes text selection via mouse and lack of being able to use HTML tags inside of localized strings. Nevertheless, problems with form elements and localization of attribute values still exist in the present.

Conclusion Link

Hopefully, a simple solution to the problem of internationalization in JavaScript will be added to the specification soon. Until then, we front-end developers will have to reinvent the wheel or adapt each other’s wheels.

While building this CSS-driven internationalization solution, I was actually inspired by ideas contained in the CSS2 specification itself. Maybe the authors already had it in mind. Who knows?

After several iterations, the solution solidified. Sure, it still has limitations. Yet its advantages, like being fully unobtrusive, will make your code substantially cleaner and easier to maintain. Hopefully, this article has helped you to understand what I did behind the scenes to achieve that.

Feel free to share your thoughts on the better-dom library GitHub18 or in the comments section below.

Front page image credits: Dmitry Baranovskiy19.

(al, il)

Footnotes Link

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19

↑ Back to top Tweet itShare on Facebook

Maksim is a freelance full-stack Web developer who lives in Minsk, Belarus. In his spare time, he likes to learn Web standards and to contribute to open source projects that could accelerate publishing new ideas via the Internet.

  1. 1

    I want to suggest another way to tackle localization on server driven apps, stop me if this isn’t the problem you’re trying to solve. You are assuming a fully client side driven application, but this problem is almost non existent if your application is localized on server side, we can simply put all strings in one object in a script generated by server, then use keys anywhere in js. In other words, I don’t think managing language is a client side issue. As for having content in data- attributes, I think this is much better. Strings and content belong there. Content maintenence is easier if developer finds it where he expects it.
    Css can have one definition, that retrieves content from html data attribute, which is already rendering the correct language. Plugin authors should get into the habit of never adding string content in line their code.

    • 2

      Well, this is tricky. Is it a good idea to rely on server-side code when you are developing a client-side plugin? I don’t think so… Just imagine that you’ll need to write what changes should be done on server-side to use a date picker widget for instance. It looks weird.

      Your solution works for fully hand-written projects without 3rd party widgets. But most of time we have to use independent plugins and construct web applications from them. Therefore I do believe that we need to have i18n APIs on client-side.

      On the other hand this set of APIs should be easy to integrate with any server-side architecture and easy to use. This is what I was trying to solve.

    • 3

      It’s common nowadays to have a staless async layer on top of your backend. Say a nodejs app which just make api calls to a propertary (*sql and RoR) backend.
      Keeping all the client logic on the client makes implementing this easier. That let you use any PaaS and without using any DB.
      To scale up or down your nodejs cluster becomes super easy because there is no data to lose.

  2. 4

    Joonas Salovaara

    June 23, 2014 3:08 pm

    Good article and a nice trick with the css lang. A small correction to the Finnish example: it’s spelled Finnish not Finish. :)

  3. 5

    Joe Watkins

    June 23, 2014 3:15 pm

    Great article! I do believe you could add Accessibility to the list of limitations. This would be the only thing holding me back from attacking localization with this approach. Here’s a good link testing screen readers and pseudo elements:

    With this in mind, how would you address accessibility with css-driven internationalization?

    • 6

      Thanks! Your point is right. I do agree with the article author that “do NOT use pseudo-elements to generate useful content” and also mentioned this in my article as well.

      There are a lot of use cases when accessibility is not important (form validation tooltips, date picker captions etc.) and where we need to touch i18n on client side. At present I’d recommend to use my trick to solve such kind of problems.

      Not sure if it’s possible to solve the accessibility problem for pseudo-elements… Need to research on that.

      • 7

        Please check the link from new “Solving Accessibility Problems” section where the solution was improved to address the accessibility problems.

  4. 8

    Matt Kaemmerer

    June 23, 2014 4:56 pm

    I love the simplicity of your approach for things like form validation. For a general purpose solution, though, I find CSS to be rather lacking.

    You hinted at one of the limitations earlier in the article: “The value of the content attribute may be … a text string (but not an HTML string)”. So I can use “Hello World” as a translation, but not “Hello World” without changing the markup. Another issue is selection. Pseudo-element text can’t be highlighted, so for instances where being able to copy/paste is important CSS is a no-go.

    • 9

      Thanks for the point, I agree that not being able to select text is a disadvantage in general. Although for my current use cases this is not a big issue – there is no reason to select form validation tooltips, date picker captions etc. Am I lucky? :)

  5. 10

    While this is a nice solution, it’s not good for production. We need internationalization in our web-apps that need to work offline, for 20+ languages. We’re pretty much forced to use XML’s.

    • 11

      Interesting, what XML’s do you mean? XSLT transforms?

      • 12

        no, we have one XML file per language. Inside we have hundreds of nodes each with a unique ID. On the HTML side we include a specific node like so: data-text=”aUniqueNodeID”
        On pageLoad we check for the current language, get the specific XML and include the text by ID into HTML using JavaScript.

        Our translators use tools where they can just include an XML, translate it and get the new file back to us.

        It’s a straightfoward process right now though setting up the inital XML (creating all the nodes) is a pain. But I don’t think there is a better solution.

        • 13

          Well, you could probably simplify the process by replacing XML with nested JSON, but the approach will be still almost the same.

          The part I want to hit with the CSS-driven i18n is simplification of localization on initial page load + AJAX content when page language is different from default. Existing solutions (and your approach as well) are too obstructive in this area.

          • 14

            we thought about using JSON a lot – I’d actually prefer it. But we’re not sure the tools used by third-party translators can handle a JSON file and compatibility needs to be there. So if I’d start a new project, I’d use JSON. But for this project and it’s sub-projects, we keep the current workflow.

            Again: your CSS solution seems fine but only for small stuff – let’s say a currency behind a price. But there is no way anyone would use this for entire paragraphs, articles or headlines.

  6. 15

    A very novel and creative approach to a problem that has been solved on the server-side for a long time. Please, continue using server-side solutions that are much better suited and will comply with SEO requirements.

    • 16

      Search engines are evaluating css and JavaScript this days. Internationalization still a issue if your backend layer needs to be stateless.

    • 17

      I do agree that for a full page localization better to use a server-side solution. But there are bunch of use-cases that better to handle on client-side, like mentioned in the article (form validation tooltips, date picker’s days of week etc.). Touching backend to implement them doesn’t make a lot of sense.

  7. 18

    It looks very ineffective, repetitive and unmaintainable.

    And in any case, CSS should define styling, not content. So while the first example with the quotation marks is fine, the rest are pretty bad in my opinion.

    On the client-side, you can implement localization using JavaScript in a much easier and scalable way. I see no reason to do that in CSS just because you can.

    • 19

      Actually there is no content in CSS in the improved version of CSS-driver internationalization.

      I’d recommend you to re-read the section “Advantages Of CSS-Driven Internationalization”. The main advantage that makes the solution to be simple is that you never need to do a call manually to switch to the current language.

  8. 20

    Cascading STYLE sheet…

    • 21

      It depends on how to look at the i18n problem. IMO different languages just REPRESENT the same content in different ways. And representation problems are usually handled by CSS.

  9. 22

    I think you’ve reinvented JS micro-templating, but with the use of CSS that does nothing other than severe limitations. Another drawback of using pseudo-elements you dit not mention, is that you actually can not use pseudo elements for anything else.

    This does not feel right, because it is not right. Internationalisation should be managed on server side. If you’re working on a static website, then it should be done with JavaScript on top of a templating engine. Working with textContent/setAttribute is not ineffective, it is very fast and it is the right way to do it.

    The “obstrusive” argument is not a real issue, but if you think the html:lang attribute watch is important for some reason, just use a Mutation Observer.

    • 23

      True, I’m moving from pseudo-elements, check the latest update.

      As for the point to move I18N on back end – there are bunch of use cases when relying on server-side doesn’t feel right. For instance when you need to develop a date picker widget, that respects any variation of days of week, months etc.

      Mutation Observers do not have a good browser support at present (check, especially on mobile.

  10. 24

    Wouldn’t it be better just to output all languages in spans or something, use JS to set a class on the html and then use css to show the required language spans and hide the rest?

    That I find better than messing with content properties, also much better for large areas of body text than using an attribute.

    Also most CMS, even enterprises grade CMSs like TYPO3 render content with just one language. Very view output more than one. Also if you application has mutiple languages the HTML generated could start to get pretty big!

    • 25

      Funny, but I did it like you proposed. Check out the section “Solving Accessibility Problems”.

      I do agree that for a full-page I18N better to use a server-side solution. On the other hand it’s good to have set of APIs that allows you to develop unobtrusive client-side plugins without worrying about the current page language. This is what I’m trying to achieve.

  11. 26

    Thanks for feedback guys. I added new “Solving Accessibility Problems” section where the solution was improved to address the accessibility problems.

  12. 27

    It’s a neat trick, but isn’t it mixing content with presentation? You could argue that content as semantics is independent of language, and the actual language could be seen as a kind of “presentational layer”, but that seems a bit far-fetched.

    Also, I see problems with authoring and maintenance. The localization would have to be handled by someone who can code CSS, or require a tool that would need to convert some other authoring format into the CSS, and keep the two in sync. That adds complexity.

    • 28

      Hey @schoschie,

      I want to clarify, that the trick is about **CSS-DRIVEN** but not about I18N in CSS. The idea is to put into CSS only logic RELATED TO SWITCHING LANGUAGES, but not localized strings themselves. If you’ll look at rules I add they are general rules.

      Therefore some words about your second statement. Localized strings is located in *.js files (not CSS). They look like below:

      DOM.importStrings(“ru”, {
      “hello”: “привет”,
      “world”: “мир”,


      Sure there is a JavaScript function call that wraps localized strings, but it’s pretty minimal. I think that it’s possible to simplify it by moving the second object argument into *.json or *.xml on demand. This is just a matter of some extra coding.

  13. 29

    TL;DR: not a good idea, do not try this at home

  14. 30

    Jacob Kelley

    June 27, 2014 5:15 pm

    I built a client side localization library called Lingual.js:

    We’ve been using it at Maker Studios with knockout and it’s been great in production.

  15. 31

    What if you support maybe 10 languages? I think it could get rather bloated if you need an attribute for every language. I think maybe just one that stores a reference to a particular string in some JSON file. When the user selects the language, you could do an ajax call and and get the correct strings and update them in the dom. On the first page load, this could be handled serverside..

    I think the css solution is unmaintainable and would get bloated rather quickly.

    • 32

      I don’t think it’s a good idea to use the concept for full page i18n. Server-side solution should be used there. The problem that I wanted to solve is localization of client-side plugins that will be unobtrusive and easy to use.

  16. 33

    What if you support maybe 10 languages? I think it could get rather bloated if you need an attribute for every language. I think maybe just one that stores a reference to a particular string in some JSON file. When the user selects the language, you could do an ajax call and and get the correct strings and update them in the dom. On the first page load, this could be handled serverside..

    I think the css solution is unmaintainable and would get bloated rather quickly.

    • 34

      Maksim Chemerisuk

      September 14, 2014 7:45 pm

      This is a set of API for client-side plugins. For a full page I18N use a backend solution.

      And of course you can populate strings on client-side asynchronously as well.

  17. 35

    Hi, Maksim! If you’re involved in localization projects and are interested in tools that can help you better manage the string translation process, you might want to check out the online platform
    It can automate the translation workflow a lot due to its API, Translation Memory and collaborative work environment.
    Good day!

  18. 36

    This is a great article and we should be seeing more of these kinds of postings. One thing though, I’d like to recommend if ever you need to fill out those unruly tax forms, you can check it through this link
    The software is free if you invite a couple of friends to try it out using this link
    This is truly a beautiful service with many advanced features such as efax subscription, digital signatures, unlimited secure online document storage, customizable templates, pdf converter, a library of 10 million tax and legal forms, and much more. They even have Ipad, iphone and android application with full functionality. They also provide customer and technical support 24/7.


↑ Back to top