Skip to content

Instantly share code, notes, and snippets.

@AzureKitsune
Created January 22, 2012 21:26
Show Gist options
  • Save AzureKitsune/1658899 to your computer and use it in GitHub Desktop.
Save AzureKitsune/1658899 to your computer and use it in GitHub Desktop.
#
#
# Nimrod's Runtime Library
# (c) Copyright 2011 Alex Mitchell
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## :Author: Alex Mitchell
##
## This module implements a way of pulling localized strings from an .ini file.
import parsecfg, tables, streams, strutils
type
TLanguage = object of TObject
Region*: string
Dialect*: string
DisplayName*: string
TLocaleData = object of TObject
ID: string
Name: string
Keys: TTable[string,string]
TLocaleManager = object of TObject
Data: seq[TLocaleData]
DefaultLanguage*: TLanguage
proc getLocaleName(): string
proc `$`*(s: TLanguage): string =
result = s.DisplayName
proc parseLanguageCode*(code: string): TLanguage =
var bits = split(code, '-')
result.Region = bits[0]
result.Dialect = bits[1]
# Add other regions to this.
when defined(windows):
proc ResolveLocaleName(lpNameToResolve: WideCString, lpLocaleName: WideCString, cchLocaleName: cint): cint {.stdcall, dynlib: "kernel32", importc: "ResolveLocaleName".}
var buf: array[0..50, TUtf16Char]
discard ResolveLocaleName(toWideCString(result.Region), cast[WideCString](addr(buf)), 85)
result.DisplayName = $cast[WideCString](addr(buf))
proc getLanguage*(): TLanguage =
result = parseLanguageCode(getLocaleName())
proc initLocaleManager*(): TLocaleManager =
result.Data = @[]
result.DefaultLanguage = getLanguage()
proc loadLocaleFile*(localeM: var TLocaleManager, file: string) =
var fileStr = newFileStream(file,fmRead)
var cfg : TCfgParser
open(cfg, fileStr, file)
var loc: TLocaleData
while true:
var e = next(cfg)
case e.kind
of cfgEof:
localeM.Data.add(loc) # add the last locale
break
of cfgSectionStart:
if loc.Name != nil:
localeM.Data.add(loc)
loc.Name = e.section
loc.Keys = initTable[string,string]()
of cfgKeyValuePair:
loc.Keys.add(e.key,e.value)
else: nil
close(cfg)
proc getLocalizedString*(loc: TLocaleManager, key: string, langname: string): string =
for lang in loc.Data:
if lang.Name == langname:
result = lang.Keys[key]
break
proc getLocalizedString*(loc: TLocaleManager, key: string): string =
result = getLocalizedString(loc, key, loc.DefaultLanguage.DisplayName)
when defined(windows):
from windows import LCID
# function below only works on Vista and up.
proc GetUserDefaultLocaleName*(lpLocaleName: WideCString, cchLocaleName: cint): cint {.stdcall, dynlib: "kernel32", importc: "GetUserDefaultLocaleName".}
proc GetUserDefaultLCID*(): LCID {.stdcall, dynlib: "kernel32", importc: "GetUserDefaultLCID".}
proc getLocaleName(): string =
#var name: array[0..86, TUtf16Char]
#var len = GetUserDefaultLocaleName(cast[WideCString](addr(name)), 85) - 1
#echo(len)
#echo(len(cast[WideCString](addr(name))))
#result = $cast[WideCString](addr(name))
var l = GetUserDefaultLCID()
# <araq_bnc> (x shr 16) and 0B1111 == sort_id
# <araq_bnc> x and 0xffff == language_id
result = "en-US"
when isMainModule:
var localeM = initLocaleManager()
echo(localeM.DefaultLanguage.DisplayName)
localeM.loadLocaleFile("myLocale.ini")
echo(localeM.getLocalizedString("welcome"))
echo(localeM.getLocalizedString("welcome", "Spanish"))
[English]
welcome="Welcome to my localized Nimrod program."
[Spanish]
welcome="Bienvenidos a mi localizada programa Nimrod."
import os
type
TUtf16Char = distinct int16
WideCString = ptr array[0.. 1_000_000, TUtf16Char]
const
utf8Encoding = 65001
proc len(w: WideCString): int =
while int16(w[result]) != 0'i16: inc result
proc MultiByteToWideChar(
CodePage: int32,
dwFlags: int32,
lpMultiByteStr: cstring,
cbMultiByte: cint,
lpWideCharStr: WideCString,
cchWideChar: cint): cint {.
stdcall, importc: "MultiByteToWideChar", dynlib: "kernel32".}
proc WideCharToMultiByte(
CodePage: int32,
dwFlags: int32,
lpWideCharStr: WideCString,
cchWideChar: cint,
lpMultiByteStr: cstring,
cbMultiByte: cint,
lpDefaultChar: cstring=nil,
lpUsedDefaultChar: pointer=nil): cint {.
stdcall, importc: "WideCharToMultiByte", dynlib: "kernel32".}
proc `$`*(s: WideCString, len: int): string =
# special case: empty string: needed because MultiByteToWideChar
# return 0 in case of error:
if len == 0: return ""
# educated guess of capacity:
var cap = len + len shr 2
result = newStringOfCap(cap)
let m = WideCharToMultiByte(
CodePage = utf8Encoding,
dwFlags = 0'i32,
lpWideCharStr = s,
cchWideChar = cint(len),
lpMultiByteStr = cstring(result),
cbMultiByte = cap)
if m == 0:
# try again; ask for capacity:
cap = WideCharToMultiByte(
CodePage = utf8Encoding,
dwFlags = 0'i32,
lpWideCharStr = s,
cchWideChar = cint(len),
lpMultiByteStr = nil,
cbMultiByte = cint(0))
# and do the conversion properly:
result = newStringOfCap(cap)
let m = WideCharToMultiByte(
CodePage = utf8Encoding,
dwFlags = 0'i32,
lpWideCharStr = s,
cchWideChar = cint(len),
lpMultiByteStr = cstring(result),
cbMultiByte = cap)
if m == 0: OSError()
setLen(result, m)
elif m <= cap:
setLen(result, m)
else:
assert(false) # cannot happen
proc `$`*(s: WideCString): string =
result = s $ s.len
proc toWideCString*(s: string): WideCString =
## free after usage with `dealloc`.
let cap = s.len+1
result = cast[wideCString](alloc0(cap * 2))
# special case: empty string: needed because MultiByteToWideChar
# return 0 in case of error:
if s.len == 0: return
# convert to utf-16 LE
let m = MultiByteToWideChar(CodePage = utf8Encoding, dwFlags = 0'i32,
lpMultiByteStr = cstring(s),
cbMultiByte = cint(s.len),
lpWideCharStr = result,
cchWideChar = cint(cap))
if m == 0: OSError()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment